package com.cygsunri.ess.task;

import com.cygsunri.common.constant.DataName;
import com.cygsunri.common.constant.DevicePsrId;
import com.cygsunri.common.constant.DeviceType;
import com.cygsunri.common.entity.ConfigConstant;
import com.cygsunri.common.service.CommonDataService;
import com.cygsunri.ess.vo.JidianEssBaseResponse;
import com.cygsunri.measurement.calculate.AbstractComplexEndureCalculateService;
import com.cygsunri.measurement.entity.MeasurementValue;
import com.cygsunri.scada.base.BasePSR;
import com.cygsunri.scada.service.ScadaMeasurementService;
import com.cygsunri.scada.service.ScadaPSRService;
import com.cygsunri.util.Base64Util;
import com.cygsunri.util.DateUtil;
import com.cygsunri.util.HMACUtil;
import com.cygsunri.util.UrlParametersUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.http.*;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 储能子站计算量
 */
@Component
@Slf4j
@ConditionalOnProperty(prefix = "task.ess", name = "sub", havingValue = "true")
@PropertySource(name = "application.yml", value = {"classpath:application.yml"}, ignoreResourceNotFound = false, encoding = "UTF-8")
public class EssSubCalcTask extends AbstractComplexEndureCalculateService {

    @Autowired
    private ScadaPSRService scadaPSRService;

    @Autowired
    private ScadaMeasurementService scadaMeasurementService;

    @Autowired
    private CommonDataService commonDataService;

    private Map<BasePSR, List<BasePSR>> pcsMap = new HashMap<>();
    private Map<BasePSR, List<BasePSR>> batteryMap = new HashMap<>();
    private Map<BasePSR, List<BasePSR>> childrenMap = new HashMap<>();
    private BasePSR sub = new BasePSR();

    @Bean
    public void essSubCalcInit() {
        sub = scadaPSRService.getDeviceByPsrId(DevicePsrId.ESS);

        List<BasePSR> subs = scadaPSRService.getDevicesByType(DeviceType.ESS_SUB.getKey());
        for (BasePSR sub : subs) {
            List<BasePSR> batteryList = scadaPSRService.getDeviceOfSubstationByType(sub.getId(), DeviceType.BATTERY.getKey());
            if (batteryList.isEmpty()) return;
            batteryMap.put(sub, batteryList);

            List<BasePSR> pcsList = scadaPSRService.getDeviceOfSubstationByType(sub.getId(), DeviceType.PCS.getKey());
            if (pcsList.isEmpty()) return;
            pcsMap.put(sub, pcsList);

            List<BasePSR> children = scadaPSRService.getDeviceOfSubstation(sub.getId());
            if (children.isEmpty()) return;
            childrenMap.put(sub, children);
        }
    }

    @Scheduled(cron = "${task.ess.cron}")
    private void essSubCalc() {
        long a = System.currentTimeMillis();

        batteryMap.forEach((sub, batteryList) -> {
            essSubSOCOrSOHCalc(sub, batteryList, DataName.SOC);
            essSubSOCOrSOHCalc(sub, batteryList, DataName.SOH);

        });
        pcsMap.forEach((sub, pcsList) -> {
            essSubPowerCalc(sub, pcsList);
            essSubKwhCalc(sub, pcsList, DataName.DAY_CHARGE);
            essSubKwhCalc(sub, pcsList, DataName.DAY_DISCHARGE);
            //essSubTotalKwhCalc(sub, pcsList, DataName.TOTAL_CHARGE);
            //essSubTotalKwhCalc(sub, pcsList, DataName.TOTAL_DISCHARGE);
            essSubEfficiencyCalc(sub);
        });
        //childrenMap.forEach(this::essDeviceStatus);

        log.info("储能子站计算量耗时{}ms", System.currentTimeMillis() - a);
    }

    /**
     * 储能SOC和SOH计算
     */
    private void essSubSOCOrSOHCalc(BasePSR sub, List<BasePSR> batteryList, String dataName) {
        Double average;
        Double totalSoc = 0d;
        Integer actualSize = 0;

        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), dataName)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), dataName);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), dataName);
            return;
        }

        for (BasePSR battery : batteryList) {
            MeasurementValue value = commonDataService.getValue(battery.getId(), dataName);
            if (!value.isInValid() && value.getData() >= 0 && value.getData() <= 100) {
                totalSoc += value.getData();
                actualSize++;
            }
        }

        if (actualSize > 0) {
            average = totalSoc / actualSize;
            this.saveMean(pair.getLeft(), average, DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
        }
    }

    /**
     * 储能功率计算
     */
    private void essSubPowerCalc(BasePSR sub, List<BasePSR> pcsList) {


        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), DataName.TOTAL_ACTIVE_POWER)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), DataName.TOTAL_ACTIVE_POWER);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), DataName.TOTAL_ACTIVE_POWER);
            return;
        }

        Double value = 0d;
        int num = 0;
        for (BasePSR pcs : pcsList) {
            MeasurementValue m = commonDataService.getValue(pcs.getId(), DataName.OUT_POWER);
            if (!m.isInValid()) {
                value += m.getData();
                num++;
            }
        }

        if (num > 0) {
            this.saveMean(pair.getLeft(), value, DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
        }
    }

    /**
     * 储能总充总放计算
     */
    private void essSubTotalKwhCalc(BasePSR sub, List<BasePSR> pcsList, String dataName) {
        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), dataName)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), dataName);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), dataName);
            return;
        }

        Double value = 0d;
        int num = 0;
        for (BasePSR pcs : pcsList) {
            MeasurementValue m = commonDataService.getValue(pcs.getId(), dataName);
            if (!m.isInValid() && m.getData() >= 0) {
                value += m.getData();
                num++;
            }
        }
        if (num > 0) {
            this.saveMean(pair.getLeft(), value, DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
        }
    }

    /**
     * 储能日充日放计算
     */
    private void essSubKwhCalc(BasePSR sub, List<BasePSR> pcsList, String dataName) {
        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), dataName)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), dataName);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), dataName);
            return;
        }

        ImmutablePair<Double, Integer> valuePair;
        ImmutablePair<String, Integer> pcsPair = scadaMeasurementService.getMeasurementID(pcsList.get(0).getId(), dataName);
        //如果PCS自带测点，直接计算，默认一种PCS点表一致，同时deviceTemplate的name与子站一致
        if (pcsPair != null) {
            valuePair = calcFromPcs(pcsList, dataName);
        } else {
            String name = dataName.equals(DataName.DAY_CHARGE) ? DataName.TOTAL_CHARGE : DataName.TOTAL_DISCHARGE;
            pcsPair = scadaMeasurementService.getMeasurementID(pcsList.get(0).getId(), name);
            if (pcsPair == null) {
                return;
            }
            valuePair = calcFromPcsDiff(pcsList, name);
        }

        if (valuePair.getRight() > 0) {
            this.saveMean(pair.getLeft(), valuePair.getLeft(), DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
        }
    }

    /**
     * PCS累加
     */
    private ImmutablePair<Double, Integer> calcFromPcs(List<BasePSR> pcsList, String dataName) {
        Double value = 0d;
        int num = 0;
        for (BasePSR pcs : pcsList) {
            MeasurementValue m = commonDataService.getValue(pcs.getId(), dataName);
            if (!m.isInValid() && m.getData() >= 0) {
                value += m.getData();
                num++;
            }
        }
        return new ImmutablePair<>(value, num);
    }

    /**
     * PCS差值累加
     */
    private ImmutablePair<Double, Integer> calcFromPcsDiff(List<BasePSR> pcsList, String name) {
        Double value = 0d;
        int num = 0;
        for (BasePSR pcs : pcsList) {
            MeasurementValue m = commonDataService.getValue(pcs.getId(), name, DateUtil.nowMilliSeconds(), 500);
            if (!m.isInValid() && m.getData() >= 0) {
                value += m.getData();
                num++;
            }
        }
        return new ImmutablePair<>(value, num);
    }

    /**
     * 储能效率计算
     */
    private void essSubEfficiencyCalc(BasePSR sub) {
        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), DataName.EFFICIENCY)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), DataName.EFFICIENCY);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), DataName.EFFICIENCY);
            return;
        }

        MeasurementValue charge = commonDataService.getValue(sub.getId(), DataName.TOTAL_CHARGE);
        MeasurementValue discharge = commonDataService.getValue(sub.getId(), DataName.TOTAL_DISCHARGE);

        if (!charge.isInValid() && charge.getData() > 0 && !discharge.isInValid() && discharge.getData() >= 0) {
            Double value = discharge.getData() / charge.getData();
            this.saveMean(pair.getLeft(), value * 100, DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
        }
    }

    /**
     * 储能设备通讯状态
     */
    private void essDeviceStatus(BasePSR sub, List<BasePSR> children) {
        for (BasePSR basePSR : children) {
            MeasurementValue communicationFault = commonDataService.getValue(basePSR.getId(), DataName.COMMUNICATION_FAULT);
            if (!communicationFault.isInValid() && communicationFault.getData() == 1) {
                setDeviceStatus(basePSR, "COMMUNICATION_FAULT");
            } else {
                setDeviceStatus(basePSR, "RUNNING");
            }
        }
    }

    /**
     * 第三方储能数据计算存储
     */
    private void essSubCalc(BasePSR sub, String dataName, Double value) {
        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), dataName)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), dataName);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), dataName);
            return;
        }
        if ( value != null) {
            this.saveMean(pair.getLeft(), value, DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
        }
    }

    /**
     * 储能年充放电量（通过累计总充放电量计算）
     */
    private void essSubYearCalc(BasePSR sub, String dataName1, String dataName2) {
        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), dataName1)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), dataName1);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), dataName1);
            return;
        }

        MeasurementValue year = commonDataService.getValue(sub.getId(), dataName2, DateUtil.nowMilliSeconds(), 503);

        if (!year.isInValid() && year.getData() != null) {
            this.saveMean(pair.getLeft(), year.getData(), DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
        }
    }
}
